iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Security

走進資安現場: JavaScript資安逆向工程超實戰系列 第 24

Day 24 逆向實戰 - Post 驗證參數 (中等)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250924/20169775ltpFcCmCqx.jpg

本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。
所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。
讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。

本文同步發佈:https://nicklabs.cc/javascript-reverse-engineering-post-payload-medium

挑戰網址

aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC85Lw==

解題過程

DevTools 被 debugger 斷住

打開 DevTools 後會直接停在debugger需要往上追蹤 Call Stack 並且 Override 排除。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775cscX01hgXC.jpg

確認請求方法

選擇請求 data/ 在 Headers 標籤下可以確認請求方法為 POST。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775mDXmmQ13Od.jpg

檢查請求參數

在 Payload 標籤下觀察請求內容,看到參數 m 和 tt,為傳送的驗證參數。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775pZ1ikekZKb.jpg

追蹤請求來源

在 Initiator 分頁檢查呼叫堆疊追蹤到 window.loadPage 函式來自 pagination9.js。

點擊 pagination9.js 並進入該對應的函式。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775liMsu9T14x.jpg

定位 ajax 呼叫

程式碼中呼叫 $[r(1414)] 經過混淆不太容易觀察到實際內容。

https://ithelp.ithome.com.tw/upload/images/20250924/201697750oZxRVD7wQ.jpg

驗證 ajax 映射

在 Console 輸入 r(1414) 得到 ajax,所以$[r(1414)] 等同於 $.ajax。

在 Console 輸入 $.ajax 並點擊函式可以跳到該函式位置。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775g0c8jnJD4D.jpg

分析自訂 ajax 包裝

繼續追蹤 n[r(1414)] = function(u){...},發現這裡重新包裝了 $.ajax。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775p9ggXa9mGK.jpg

函式呼叫關聯

先下斷點觀察,可以觀察到呼叫鏈中使用 t[e(613)],透過 n(r) 方式轉換並呼叫函式。

所以實際上需要觀察的是n的函式,將滑鼠放在n上會顯示函式資訊,並且點擊進入該函式。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775X8HInlyUnv.jpg

switch-case 生成資料

這個函式也是經過混淆的。

https://ithelp.ithome.com.tw/upload/images/20250924/201697757FmxsnI45v.jpg

switch-case 執行順序

透過 r(1034) 取出的字串 "1|6|7|2|0|3|4|5" 控制 switch-case 的執行順序。

https://ithelp.ithome.com.tw/upload/images/20250924/201697754zS61h7TuW.jpg

所以實際上的執行邏輯如下,但這還是混淆過的程式碼,需要進一步解混淆。

var f = (new Date)[r(2244)]();
n[r(516)] = n.headers || {};
n[r(516)][r(481)] = t.fFUZS;
n[r(1939)] = n[r(1939)] || {};
n.data.m = t.MqmaW(c, t[r(1881)](t[r(489)], f));
n[r(1939)].tt = btoa(f);
n[r(1939)] = JSON[r(827)](n.data);

解混淆後的程式碼如下

var f = (new Date).getTime();
n.headers = n.headers || {};
n.headers['Content-Type'] = 'application/json';
n.data = n.data || {};
n.data.m = c('9527' + f);
n.data.tt = btoa(f);
n.data = JSON.stringify(n.data);

解完混淆可以得知兩個參數實際邏輯。

m 是透過 c('9527' + 時間戳) 產生的。

tt 則是將時間戳使用base64編碼。

觀察並進入 c 函式內部

滑鼠移到變數 c 可以顯示該函式資訊,並可直接跳轉到程式碼定義位置。

https://ithelp.ithome.com.tw/upload/images/20250924/20169775npZVIy2muc.jpg

確認加密簽名生成

最後可以定位到產生簽名的函式,看到使用 HmacSHA1 並結合特定字串運算,這裡就是最終生成驗證參數的關鍵位置。

https://ithelp.ithome.com.tw/upload/images/20250924/201697753g6LoWSjG1.jpg

完整程式碼

const CryptoJS = require('crypto-js')

function generatorPostM(time) {
    const text = `9527${time}`
    return CryptoJS.HmacSHA1(text, "xxxooo").toString();
}

function generatorPostTt(time) {
    return btoa(`${time}`)
}

const getPage = async(page) => {
    const time = new Date().getTime();

    const postM = generatorPostM(time);
    const postT = generatorPostTt(time)

    const response = await fetch(`https://xxxxxxxxxx/api/problem-detail/9/data/`, {
        "headers": {
            "accept": "*/*",
            "accept-language": "zh-TW,zh;q=0.9,en;q=0.8,en-US;q=0.7",
            "cache-control": "no-cache",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "sec-ch-ua": "\"Not;A=Brand\";v=\"99\", \"Google Chrome\";v=\"139\", \"Chromium\";v=\"139\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"macOS\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "cookie": "sessionid=xxxxxxxxxx",
            "Referer": "https://xxxxxxxxxx/problem-detail/9/",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
        },
        "body": JSON.stringify({
            "page": page,
            "m": postM,
            "tt": postT,
        }),
        "method": "POST",
    });

    let json = await response.json()

    return json.current_array.reduce((a, b) => a + b, 0);
}

const run = async() => {
    let total = 0;
    for(let i = 1; i <= 20; i++){
        total += (await getPage(i))
    }

    console.log(`total: ${total}`)
}

run()

Github 原始碼

https://github.com/mrnick6886/ScrapingChallenges/blob/main/mashangpa/9.js


上一篇
Day 23 逆向實戰 - Header 驗證參數 + Url 驗證參數 + Response加密 (中等)
下一篇
Day 25 逆向實戰 - URL 驗證參數 (中等)
系列文
走進資安現場: JavaScript資安逆向工程超實戰26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言